package hotel.quartz.oversea.job.switchs;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import hotel.base.oversea.constant.CommonConstants;
import hotel.base.oversea.constant.CommonUrlConstants;
import hotel.base.oversea.constant.SwitchConstants;
import hotel.base.oversea.constant.quartz.UrlSwitchsEnums;
import hotel.base.oversea.utils.Result;
import hotel.quartz.oversea.config.service.RedisService;
import hotel.quartz.oversea.job.base.BaseJob;
import hotel.quartz.oversea.service.JobAndTriggerService;
import hotel.quartz.oversea.util.JobRestTemplateUtils;
import hotel.quartz.oversea.vo.switchs.DltBasicRoomTypeVo;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.quartz.JobExecutionContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.ObjectUtils;
import org.springframework.web.client.RestTemplate;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 *  Switch Hotel Room Master Job
 * </p>
 *
 * @description: Switch Hotel Room Master Job
 * @author: qiuyy
 * @date: 2020-05-26 13:22
 * @version: V1.0
 */
@Slf4j
public class SwitchHotelRoomMasterJob implements BaseJob {
    private String jobGroupName = SwitchConstants.JOB_GROUP_NAME_SWITCHS;
    private String jobClassName = this.getClass().getName();
    private String timestamp = "Shrmj"+String.valueOf(System.currentTimeMillis());
    private int thisRedisCount = SwitchConstants.REDIS_SW_MASTER_ROOM_TYPE_WAIT_COUNT;
    private String redisWaitSaveKey = SwitchConstants.REDIS_SW_MASTER_ROOM_TYPE_WAIT_SAVE;
    private String redisSwHotelChildIdRoomNow = SwitchConstants.REDIS_SWITCH_HOTEL_CHILD_ID_ROOM_NOW;
    private String redisSwHotelChildIds = SwitchConstants.REDIS_SWITCH_HOTEL_CHILD_IDS;
    private String redisSwHotelChildIdsRoomHas = SwitchConstants.REDIS_SWITCH_HOTEL_CHILD_IDS_ROOM_HAS;

    private String SwitchHotelRoomMasterJobProcessIngRedisHead = "SwitchHotelRoomMasterJobProcessIngRedisHead";

    @Autowired
    private JobAndTriggerService jobAndTriggerService;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RedisService redisService;
    @Autowired
    private RestTemplate restTemplate;
    /**
     * 母酒店物理房型信息，每十秒跑一次（接口要求：每分钟调用不得超过10次）
     *
     * 结果：上送一个子酒店id，返回所有母物理房型
     *
     *
     * 获取和保存分两步，如果数据库没有母物理房型数据，需排查
     *
     *
     * @param jobExecutionContext
     */
    @Override
    @SneakyThrows
    public void execute(JobExecutionContext jobExecutionContext) {
        log.info("Switch Hotel Room Master Job {} >>>>>>>> 携程物理房型获取执行时间: {}", timestamp, DateUtil.now());
       // String host = CommonUrlConstants.SWITCHS_HOST;
        String host= CommonUrlConstants.SWITCHS_ONLINE_HOST;
        String roomMethodGetUrl = host + UrlSwitchsEnums.room.GET_ROOM_MASTER.getValue();
        String saveUrl = host + UrlSwitchsEnums.room.SAVE_ROOM_MASTER.getValue();//+"?access_token="+accessToken;
        boolean check = checkSaveRedisData(saveUrl,false);
        if(!check){
            int hotelId = getNowCreateSubHotelId();
            if(-1!=hotelId){

                String pro = (String)redisTemplate.opsForValue().get(SwitchHotelRoomMasterJobProcessIngRedisHead+hotelId);
                if(null != pro){
                    log.info("Switch Hotel Room Static Job {} >>>>>>>> 携程静态物理房型,,当前酒店已存在在执行的线程，跳过: {}，{}",timestamp,pro,SwitchHotelRoomMasterJobProcessIngRedisHead+hotelId);
                }else{
                    //当前流程标识
                    redisTemplate.opsForValue().set(SwitchHotelRoomMasterJobProcessIngRedisHead+hotelId,"1",5L, TimeUnit.SECONDS);
                    requestHotelRoomInfoByHotelId(saveUrl,roomMethodGetUrl,hotelId);
                }

            }else{
                //opsValueList返回空 则无新创建子酒店需处理
                log.info("Switch Hotel Room Master Job {} >>>>>>>>  携程物理房型 无新创建子酒店需处理,opsValueList返回空 {}", timestamp, DateUtil.now());
                //最后一次调用缓存保存
                checkSaveRedisData(saveUrl,true);
                //endingJob();
                log.info("Switch Hotel Room Static Job {} >>>>>>>> 拉取母物理房型，未获取到缓存队列中的新创建子酒店数据，当前批次定时器跳过，人为判断是否需要关闭定时器: {}",timestamp, DateUtil.now());
            }
        }
        log.info("Switch Hotel Room Master Job {} >>>>>>>> 携程物理房型获取结束时间: {}", timestamp, DateUtil.now());
    }

    public int getNowCreateSubHotelId(){
        //获取当前最近创建的子酒店的id
        Long size = redisTemplate.opsForList().size(SwitchConstants.CREATE_SUB_FOR_ROOM_MASTER_JOB);
        if(size==0){
            log.info("Switch Hotel Room Master Job {} >>>>>>>> 携程物理房型 redisTemplate.opsForList().size = 0", timestamp);
            return -1;
        }
        String subHotelId = (String)redisTemplate.opsForList().rightPop(SwitchConstants.CREATE_SUB_FOR_ROOM_MASTER_JOB);//subHotelId
        log.info("Switch Hotel Room Master Job {} >>>>>>>> 携程物理房型 getNowCreateSubHotelId,size={},time={},ids={}", timestamp,size, DateUtil.now(),subHotelId);
        if(!ObjectUtils.isEmpty(subHotelId)){
            return Integer.valueOf(subHotelId);
        }
        return -1;
    }

    /**
     * 校验是否需要保存缓存的数据
     * @param saveUrl
     * @return
     */
    private boolean checkSaveRedisData(String saveUrl,boolean go) {
        log.info("Switchs Basic Room Type Job {} 携程物理房型定时器定时器进入方法 checkSaveRedisData",timestamp);
        Map<String,List<DltBasicRoomTypeVo>> getInfos = getRedisWaitData();
        if(go){
            boolean result = requestSaveWaitDataApi(saveUrl,getInfos);
            return result;
        }else{
            if(null != getInfos && getInfos.size()>=thisRedisCount){
                //缓存酒店数据达到大于等于thisRedisCount，则执行保存到数据库操作，可能大于thisRedisCount比如数据操作保存失败还原加入到redis
                boolean result = requestSaveWaitDataApi(saveUrl,getInfos);
                return result;
            }else{
                return false;
            }
        }

    }
    /**
     * 调用保存缓存数据集合接口
     * @param saveUrl
     * @param redisWaitDataSaveMap
     * @return
     */
    private boolean requestSaveWaitDataApi(String saveUrl,Map<String,List<DltBasicRoomTypeVo>> redisWaitDataSaveMap) {
        if(null == redisWaitDataSaveMap || redisWaitDataSaveMap.size()==0){
            log.warn("Switchs Basic Room Type Job {} 携程物理房型定时器进入方法 requestSaveWaitDataApi 待保存数据redisWaitDataSaveMap为空",timestamp);
            return true;//该定时器特殊处理，返回true
        }
        log.info("Switchs Basic Room Type Job {} 携程物理房型定时器进入方法 requestSaveWaitDataApi",timestamp);
        Map map = new HashMap();
        map.put("dltBasicRoomTypeVosMap",redisWaitDataSaveMap);
        //调用前,删除当前缓存的酒店数据，让下一次定时器正常查询上游接口。若调用结果失败则还原加入缓存
        deleteRedisWaitSaveData();
        //发起请求
        Result response = JobRestTemplateUtils.post(saveUrl, JSON.toJSONString(map),restTemplate);
        if(response.getCode()== CommonConstants.SUCCESS){
            log.info("Switchs Basic Room Type Job {} 携程物理房型定时器 requestSaveWaitDataApi 保存数据成功",timestamp);
            return true;
        }else{
            log.error("Switchs Basic Room Type Job {} 携程物理房型定时器 requestSaveWaitDataApi 异常错误: {}",timestamp,response);
            backPutRedisWaitSaveData(redisWaitDataSaveMap);
            return false;
        }
    }
    /**
     * 还原追加回去待保存数据
     * @return
     */
    public void backPutRedisWaitSaveData(Map<String,List<DltBasicRoomTypeVo>> redisWaitDataMap){
        log.info("Switchs Basic Room Type Job {} 携程物理房型定时器进入方法 backPutRedisWaitSaveData 还原追加当前酒店信息缓存 ",timestamp);
        //还原redis数据
        Map<String,List<DltBasicRoomTypeVo>> getRedisMap = (Map<String,List<DltBasicRoomTypeVo>>)redisService.get(redisWaitSaveKey);
        if(null != getRedisMap && getRedisMap.size()>0){
            getRedisMap.putAll(redisWaitDataMap);
        }else{
            getRedisMap = redisWaitDataMap;
        }
        redisService.set(redisWaitSaveKey,getRedisMap);
    }

    /**
     * 请求酒店物理房型接口
     * @param url
     * @param hotelId
     */
    private void requestHotelRoomInfoByHotelId(String saveUrl,String url,int hotelId) {
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("hotelId", hotelId);
//        params.put("access_token",token);
        log.info("Switch Hotel Room Master Job 携程物理房型获取进入方法requestHotelRoomInfoByHotelId: {}", JSON.toJSONString(params));
        //发起请求
        Result response = JobRestTemplateUtils.get(url,params,restTemplate);
        if(response.getCode()== CommonConstants.SUCCESS){
            if(null!= response.getData()){
                //解析响应结果
                String datajson = JSON.toJSONString(response.getData());
                List<DltBasicRoomTypeVo> dltBasicRoomTypeVoList = (List<DltBasicRoomTypeVo>) JSON.parseObject(datajson, List.class);
                if(null != dltBasicRoomTypeVoList && dltBasicRoomTypeVoList.size()>0){
                    //有数据，保存到redis待提交保存
                    //查询到数据，存入redis
                    Map<String,List<DltBasicRoomTypeVo>> dataMap = new HashMap();
                    dataMap.put(String.valueOf(hotelId),dltBasicRoomTypeVoList);
                    boolean setResultRedis = setPutRedisWaitData(dataMap);
                    if(setResultRedis){
                        //成功update并判断是否endingJob
                        //successUpdateDeleteRedisHasIdsMap(saveUrl);
                    }else{
                        //removeHasIdsMap(getRedisChildHotelIdNow());

                        //还原下次继续操作
                        redisTemplate.opsForList().leftPush(SwitchConstants.CREATE_SUB_FOR_ROOM_MASTER_JOB,hotelId);
                        log.info("Switchs Basic room Job 该酒店物理房型 savePutRedisWaitData 异常: {},{}",response, JSON.toJSONString(params));
                    }
                }else{
                    //无数据，当前子酒店无物理房型，跳过，更新下一个子酒店
                    log.info("Switchs Basic room Job {} 该酒店物理房型数据为空: {},{}",response, JSON.toJSONString(params));
                    //successUpdateDeleteRedisHasIdsMap(saveUrl);
                }
            }else{
                //无数据，当前子酒店无物理房型，跳过，更新下一个子酒店
                log.info("Switchs Basic room Job 该酒店物理房型数据为空: {},{}",response, JSON.toJSONString(params));
                //uccessUpdateDeleteRedisHasIdsMap(saveUrl);
            }
        }else{
            //endingJob(false);
            //removeHasIdsMap(getRedisChildHotelIdNow());
            //还原下次继续操作
            redisTemplate.opsForList().leftPush(SwitchConstants.CREATE_SUB_FOR_ROOM_MASTER_JOB,hotelId);
            log.error("Switchs Basic room Job 获取携程酒店物理房型信息异常错误: {},{}",response, JSON.toJSONString(params));
        }

        //释放流程标识
        redisTemplate.delete(SwitchHotelRoomMasterJobProcessIngRedisHead+hotelId);
    }
    /**
     * 成功请求酒店房型接口后，合法酒店房型数据，追加进缓存中
     */
    private boolean setPutRedisWaitData(Map<String, List<DltBasicRoomTypeVo>> mapRealRoomVoMap) {
        Map<String,List<DltBasicRoomTypeVo>> getRedisMap = getRedisWaitData();
        if(null != getRedisMap && getRedisMap.size()>0){
            getRedisMap.putAll(mapRealRoomVoMap);
        }else{
            getRedisMap = mapRealRoomVoMap;
        }
        boolean result = redisService.set(redisWaitSaveKey,getRedisMap);
        return result;
    }


    /**
     * 结束定时器
     */
    public void endingJob(){
        //已最后一页，暂停定时器,删除rediskey
        /*
        version qyy 先不做暂停
        JobForm jobForm = new JobForm();
        jobForm.setJobClassName(jobClassName);
        jobForm.setJobGroupName(jobGroupName);
        try {
            //deleteRedisHasHotelIds();
            jobAndTriggerService.pauseJob(jobForm);
            log.warn("Switch Hotel Room Master Job 执行暂停！: {},{}",jobGroupName,jobClassName);
        } catch (SchedulerException e) {
            log.error(e.getMessage(),e);
        }*/
    }
    /**
     * 获取保存在缓存中的待save的数据
     */
    private Map<String,List<DltBasicRoomTypeVo>> getRedisWaitData() {
        return (Map<String,List<DltBasicRoomTypeVo>>)redisService.get(redisWaitSaveKey);
    }
    /**
     * 删除缓存的待保存数据
     * @return
     */
    public void deleteRedisWaitSaveData(){
        log.info("Switchs Basic Room Type Job {} 携程物理房型定时器进入方法 deleteRedisWaitSaveData 删除当前携程物理房型定时器定时器数据缓存",timestamp);
        redisTemplate.delete(redisWaitSaveKey);
    }
}
